using System;
using System.Collections;

namespace ELS.Configuration
{
	#region HashList
	/// <summary>
	/// Represents a composite collection of key-and-value pairs that are accessible by key and by index.
	/// </summary>
	public class HashList : IDictionary, ICollection, IEnumerable, ICloneable
	{
		#region variables
		private Hashtable _innerHash = null;
		private ArrayList _innerList = null;

		// A load factor of 1.0 is the best balance between speed and size.
		private const float LOAD_FACTOR = 1.0f;

		#region DID YOU KNOW ?
		/*
		"The Hashtable class contains a private member variable called loadFactor 
		that specifies the maximum ratio of items in the Hashtable to the total number of slots. 
		A loadFactor of, say, 0.5, indicates that at most the Hashtable can only have half of its slots
		filled with items and the other half must remain empty.
		
		In an overloaded form of the Hashtable's constructor, you can specify a loadFactor
		value between 0.1 and 1.0. Realize, however, that whatever value you provide, 
		it is scaled down 72%, so even if you pass in a value of 1.0 the Hashtable class's actual 
		loadFactor will be 0.72. The 0.72 was found by Microsoft to be the optimal load factor, 
		so consider using the default 1.0 load factor value (which gets scaled automatically to 0.72).
		Therefore, you would be encouraged to use the default of 1.0 (which is really 0.72).

		One would wonder why, if they wanted the values to be between 0.072 and 0.72, why not make that the legal range? 
		
		The Microsoft team that worked on the Hashtable class shared their reason for this decision.
		Specifically, the team found through empirical testing that values greater than 0.72
		seriously degraded the performance. They decided that the developer using the Hashtable 
		would be better off if  they didn't have to remember a seeming arbitrary value in 0.72, 
		but instead just had to remember that a value of 1.0 gave the best results. 
		So this decision, essentially, sacrifices functionality a bit, 
		but makes the data structure easier to use and will cause fewer headaches 
		in the developer community." - Scott Mitchell
 		 
		Source : http://msdn.microsoft.com/library/en-us/dnvs05/html/datastructures20_2.asp?frame=true&_r=1 
		*/
		#endregion

		#endregion variables

		#region public events
		/// <summary>
		/// Occurs when the list changes or an item in the list changes.
		/// </summary>
		public event ListChangedEventHandler ListChanged;
		#endregion public events

		#region constructors
		/// <summary>
		/// Creates an empty HashList with the default initial capacity and using the default load factor, the default hash code provider and the default comparer.
		/// </summary>
		public HashList() : this ( 0 )
		{
		}
		/// <summary>
		/// Creates an empty HashList with the specified initial capacity and using the default load factor, the default hash code provider and the default comparer.
		/// </summary>
		/// <param name="capacity">The approximate number of elements that the <see cref="ELS.Configuration.HashList"/> can initially contain. </param>
		public HashList(int capacity) : this ( capacity, LOAD_FACTOR )
		{
		}

		/// <summary>
		/// Creates an empty HashList with the specified initial capacity and using the specified load factor, the default hash code provider and the default comparer.
		/// </summary>
		/// <param name="capacity">The approximate number of elements that the <see cref="ELS.Configuration.HashList"/> can initially contain. </param>
		/// <param name="loadFactor">A number in the range from 0.1 through 1.0 indicating the maximum ratio of elements to buckets. </param>
		public HashList( int capacity, float loadFactor )
		{
			_innerHash = new Hashtable( capacity , loadFactor);

			// the default capacity for an ArrayList is 16. 
			// If the specified initial capacity is zero, the default initial capacity is used.
			_innerList = new ArrayList( capacity );
		}
		#endregion constructors

		#region IDictionary Members

		/// <summary>
		/// Gets a value indicating whether the HashList is read-only.
		/// </summary>
		public bool IsReadOnly
		{
			get { return _innerHash.IsReadOnly; }
		}

		/// <summary>
		/// Returns an <see cref="System.Collections.IDictionaryEnumerator"/> that can iterate through the HashList.
		/// </summary>
		/// <returns>An <see cref="System.Collections.IDictionaryEnumerator"/> for the HashList.</returns>
		public IDictionaryEnumerator GetEnumerator()
		{
			return _innerHash.GetEnumerator();
		}

		/// <summary>
		/// Gets or sets the value associated with the specified key. 
		/// </summary>
		public object this[object key]
		{
			get	{ return _innerHash[ key] ; }
			set 
			{
				_innerHash[ key] = value; 
				OnListChanged(new ListChangedEventArgs( ListChangedType.ItemChanged, _innerList.IndexOf( key ) ));
			}
		}

		/// <summary>
		/// Removes the element with the specified key from the HashList.
		/// </summary>
		/// <param name="key">The key of the element to remove</param>
		public void Remove(object key)
		{
			RemoveAt( _innerList.IndexOf( key ) );
		}

		/// <summary>
		/// Determines whether the HashList contains a specific key.
		/// </summary>
		/// <param name="key">The key to locate in the <see cref="ELS.Configuration.HashList"/>.</param>
		/// <returns>true if the <see cref="ELS.Configuration.HashList"/> contains an element with the specified key; otherwise, false.</returns>
		public bool Contains(object key)
		{
			return _innerHash.Contains( key );
		}

		/// <summary>
		/// Removes all elements from the HashList.
		/// </summary>
		public void Clear()
		{
			_innerHash.Clear();
			_innerList.Clear();
		}

		/// <summary>
		/// Gets an <see cref="System.Collections.ICollection"/> containing the values in the HashList.
		/// </summary>
		public ICollection Values
		{
			get { return _innerHash.Values; }
		}

		/// <summary>
		/// Adds an element with the specified key and value into the <see cref="ELS.Configuration.HashList"/>.
		/// </summary>
		/// <param name="key">The key of the element to add.</param>
		/// <param name="value">The value of the element to add. The value can be a null reference (Nothing in Visual Basic).</param>
		public void Add(object key, object value)
		{
			_innerHash.Add( key, value );
			int index = _innerList.Add( key );
			OnListChanged(new ListChangedEventArgs( ListChangedType.ItemAdded, index ));
		}

		/// <summary>
		/// Gets an <see cref="System.Collections.ICollection"/> containing the keys in the HashList.
		/// </summary>
		public ICollection Keys
		{
			get { return _innerHash.Keys; }
		}

		/// <summary>
		/// Gets a value indicating whether the HashList has a fixed size.
		/// </summary>
		public bool IsFixedSize
		{
			get { return _innerHash.IsFixedSize; }
		}

		#endregion

		#region ICollection Members
		/// <summary>
		/// Gets a value indicating whether access to the HashList is synchronized (thread-safe).
		/// </summary>
		public bool IsSynchronized
		{
			get { return _innerHash.IsSynchronized; }
		}

		/// <summary>
		/// Gets the number of key-and-value pairs contained in the HashList.
		/// </summary>
		public int Count
		{
			get { return _innerHash.Count; }
		}

		/// <summary>
		/// Copies the <see cref="ELS.Configuration.HashList"/> elements to a one-dimensional <see cref="System.Array"/> instance at the specified index.
		/// </summary>
		/// <param name="array">The one-dimensional Array that is the destination of the 
		/// <see cref="System.Collections.DictionaryEntry"/> objects copied from <see cref="ELS.Configuration.HashList"/>. 
		/// The <see cref="System.Array"/> must have zero-based indexing. </param>
		/// <param name="index">The zero-based index in array at which copying begins.</param>
		public void CopyTo(Array array, int index)
		{
			_innerHash.CopyTo( array, index );
		}

		/// <summary>
		/// Gets an object that can be used to synchronize access to the HashList.
		/// </summary>
		public object SyncRoot
		{
			get { return _innerHash.SyncRoot; }
		}

		#endregion

		#region IEnumerable Members

		/// <summary>
		/// Returns an  <see cref="System.Collections.IEnumerator"/> that can iterate through the HashList.
		/// </summary>
		/// <returns>An <see cref="System.Collections.IEnumerator"/> for the <see cref="ELS.Configuration.HashList"/>.</returns>
		IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return _innerHash.GetEnumerator();
		}

		#endregion

		#region ICloneable Members

		/// <summary>
		/// Creates a shallow copy of the HashList.
		/// </summary>
		/// <returns>A shallow copy of the <see cref="ELS.Configuration.HashList"/>.</returns>
		public object Clone()
		{
			HashList hList = new HashList(this.Count);
			hList._innerHash = this._innerHash.Clone() as Hashtable;
			hList._innerList = this._innerList.Clone() as ArrayList;
			return hList;
		}

		#endregion
 
		#region Composite Members

		/// <summary>
		/// Gets or sets the element at the specified index. In C#, this property is the indexer for the HashList class.
		/// </summary>
		public object this[int index]
		{
			get { return _innerHash [ _innerList [index] ];    }
			set	
			{ 
				_innerHash [ _innerList [ index ] ] = value; 
				OnListChanged(new ListChangedEventArgs( ListChangedType.ItemChanged, index ));
			}
		}

		/// <summary>
		/// Removes the element at the specified index of the HashList.
		/// </summary>
		/// <param name="index">The zero-based index of the element to remove.</param>
		public void RemoveAt(int index)
		{
			object key = _innerList [ index ];
			_innerHash.Remove( key );
			_innerList.RemoveAt( index );
			OnListChanged(new ListChangedEventArgs( ListChangedType.ItemDeleted, -1, index ));
		}

		/// <summary>
		/// Returns the zero-based index of the specified key in the HashList.
		/// </summary>
		/// <param name="key">The key to locate in the <see cref="ELS.Configuration.HashList"/>.</param>
		/// <returns>The zero-based index of key, if key is found in the <see cref="ELS.Configuration.HashList"/>; otherwise, -1.</returns>
		public int IndexOfKey(object key)
		{
			return _innerList.IndexOf( key );
		}

		/// <summary>
		/// Returns the zero-based index of the specified value in the HashList.
		/// </summary>
		/// <param name="value">The value to locate in the <see cref="ELS.Configuration.HashList"/>.</param>
		/// <returns>The zero-based index of value, if value is found in the <see cref="ELS.Configuration.HashList"/>; otherwise, -1.</returns>
		public int IndexOfValue(object value)
		{
			IDictionaryEnumerator enumerator = _innerHash.GetEnumerator();
			while (enumerator.MoveNext())
			{
				if ( enumerator.Value == value )
					return _innerList.IndexOf( enumerator.Key );
			}
			return -1;
		}

		/// <summary>
		/// Determines whether the HashList contains a specific key.
		/// </summary>
		/// <param name="key">The key to locate in the <see cref="ELS.Configuration.HashList"/>.</param>
		/// <returns>true if the <see cref="ELS.Configuration.HashList"/> contains an element with the specified key; otherwise, false.</returns>
		public bool ContainsKey( object key )
		{
			return _innerHash.ContainsKey( key );
		}

		/// <summary>
		/// Determines whether the HashList contains a specific value.
		/// </summary>
		/// <param name="value">The value to locate in the <see cref="ELS.Configuration.HashList"/>.</param>
		/// <returns>true if the <see cref="ELS.Configuration.HashList"/> contains an element with the specified value; otherwise, false.</returns>
		public bool ContainsValue( object value )
		{
			return _innerHash.ContainsValue( value );
		}

		/// <summary>
		/// Gets the key at the specified index of the HashList.
		/// </summary>
		/// <param name="index">The zero-based index of the key to get.</param>
		/// <returns>The key at the specified index of the <see cref="ELS.Configuration.HashList"/>.</returns>
		public object GetKey( int index )
		{
			return _innerList [ index ];
		}

		/// <summary>
		/// Gets the value at the specified index of the HashList.
		/// </summary>
		/// <param name="index">The zero-based index of the value to get.</param>
		/// <returns>The value at the specified index of the <see cref="ELS.Configuration.HashList"/>.</returns>
		public object GetByIndex ( int index )
		{
			return this[ index ];
		}
		#endregion

		#region protected methods
		/// <summary>
		/// Raises the <see cref="ELS.Configuration.HashList.ListChanged"/> event.
		/// </summary>
		/// <param name="e">A <see cref="ELS.Configuration.ListChangedEventArgs"/> that contains the event data.</param>
		protected void OnListChanged( ListChangedEventArgs e)
		{
			if ( ListChanged != null )
				ListChanged(this, e );
		}
		#endregion protected methods
	}
	#endregion HashList
}
